package com.huang.filter;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.huang.constant.Common;
import com.huang.enums.UserCodeEnum;
import com.huang.resp.CommonResult;
import com.huang.security.properties.SecurityProperties;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.Payload;
import lombok.SneakyThrows;
import net.minidev.json.JSONObject;
import org.apache.logging.log4j.util.Strings;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.List;

/**
 * 实现gateway 全局过滤器
 *
 * @author han
 * @since 2022-01-12 14:52:40
 */
@Component
public class GatewayFilter implements GlobalFilter, Ordered {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private SecurityProperties securityProperties;

    @Override
    @SneakyThrows
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String requestUrl = exchange.getRequest().getPath().value();
        //1、白名单放行，比如授权服务、静态资源.....
        if (checkUrls(securityProperties.getWhiteList(),requestUrl)){
            return chain.filter(exchange);
        }

        //2、 检查token是否存在
        String token = getToken(exchange);
        if (StrUtil.isBlank(token)) {
            return invalidTokenMono(exchange);
        }

        //3 判断是否是有效的token
        // 解析token
        JWSObject jwsObject;
        try {
            //解析token，使用tokenStore
            jwsObject = JWSObject.parse(token.replace(Common.Token.BEARER, ""));
            Payload payload = jwsObject.getPayload();
            JSONObject jsonObject = payload.toJSONObject();
            //令牌的唯一ID
            String jti = jsonObject.get(Common.Token.JWT_JTI).toString();
            // 查看黑名单中是否存在这个jti，如果存在则这个令牌不能用
            Boolean hasKey = redisTemplate.hasKey(Common.Token.JWT_JTI_BLACK_PREFIX + jti);
            if (null != hasKey && hasKey) {
                return invalidTokenMono(exchange);
            }

            //将解析后的token加密放入请求头中，方便下游微服务解析获取用户信息
            //放入请求头中
            ServerHttpRequest tokenRequest = exchange.getRequest().mutate().header(Common.Token.JWT_PAYLOAD_KEY, URLEncoder.encode(jsonObject.toString(), "UTF-8")).build();
            ServerWebExchange build = exchange.mutate().request(tokenRequest).build();
            return chain.filter(build);
        } catch (ParseException e) {
            //解析token异常，直接返回token无效
            return invalidTokenMono(exchange);
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * 对url进行校验匹配
     */
    private boolean checkUrls(List<String> urls, String path){
        AntPathMatcher pathMatcher = new AntPathMatcher();
        for (String url : urls) {
            if (pathMatcher.match(url, path))
                return true;
        }
        return false;
    }

    /**
     * 从请求头中获取Token
     */
    private String getToken(ServerWebExchange exchange) {
        String tokenStr = exchange.getRequest().getHeaders().getFirst(Common.Token.TOKEN_HEADER_NAME);
        if (StrUtil.isBlank(tokenStr) || !StrUtil.startWithIgnoreCase(tokenStr, Common.Token.BEARER)) {
            return null;
        }

        // 解析JWT获取jti，以jti为key判断redis的黑名单列表是否存在，存在则拦截访问
        String token = StrUtil.replaceIgnoreCase(tokenStr, Common.Token.BEARER, Strings.EMPTY);
        if (StrUtil.isBlank(token)) {
            return null;
        }
        return token;
    }

    /**
     * 无效的token
     */
    private Mono<Void> invalidTokenMono(ServerWebExchange exchange) {
        return buildReturnMono(CommonResult.error(UserCodeEnum.TOKEN_ERROR), exchange);
    }


    private Mono<Void> buildReturnMono(CommonResult resultMsg, ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        byte[] bits = JSON.toJSONString(resultMsg).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add("Content-Type", "application/json;charset:utf-8");
        return response.writeWith(Mono.just(buffer));
    }
}
